﻿using LxBasic;
using LxCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace LxCFD
{
    public class GraphDrawer
    {
        public GraphDrawer(Graph graph)
        {
            this.Graph = graph;
        }
        protected Graph Graph;

        public ColorRange ColorRange;

        public ModelVisual3D ModelVisual;
        private PlaneFactory planFactory = new PlaneFactory();
        private PlaneArrowFactory arrowFactory = new PlaneArrowFactory();
        public void CreateGraph()
        {
            switch (Graph.GraphType)
            {
                case GraphTypes.网格图:
                    CreateGraph_Mesh();
                    break;
                case GraphTypes.等值图:
                    CreateGraph_Scalar();
                    break;
                case GraphTypes.矢量图:
                    CreateGraph_Vector();
                    break;
            }
        }
        public void CreateGraph_Vector()
        {
            ColorRange = new ColorRange();
            VectorFinder scalarf = new VectorFinder(Graph.VectorType);
            //计算min max值
            MinMaxRange range = new();
            MultiThreads.RunParallelTask((threadInfo) =>
            {
                MinMaxRange tempR = new MinMaxRange();
                foreach (var z in Graph.SelectedZone())
                {
                    ICellVarArr<Vector3d> scalar = scalarf.GetCellVarArr(z.Inner);
                    if (scalar != null)
                    {
                        (int i1, int endp1) = z.Inner.CellIdx.Split(threadInfo);
                        for (; i1 < endp1; i1++)
                        {
                            tempR.Include(scalar.GetCellVar(i1).Length());
                        }
                    }
                }
                lock (MultiThreads.GlobalLock)
                {
                    range.Include(tempR);
                }
            });
            if (range.IsEmpty()) return;
            ColorRange.Range = range;
            //判断属于哪个分段并添加到相应的mesh中
            MeshGeometry3D[] meshGeometry3Ds = new MeshGeometry3D[ColorRange.Segment];//最后加一个gray色
            for (int i1 = 0; i1 < meshGeometry3Ds.Length; i1++)
            {
                meshGeometry3Ds[i1] = new MeshGeometry3D()
                {
                    Positions = new Point3DCollection(),
                    TriangleIndices = new Int32Collection()
                };
            }
            PlaneFactory planeFactory = new PlaneFactory();
            double segRange = range.GetRange() == 0 ? 1 : range.GetRange() / ColorRange.Segment;
            int maxValidId = ColorRange.Segment - 1;
            double scale = Graph.ScaleFactor;
            foreach (var z in Graph.SelectedZone())
            {
                ICellVarArr<Vector3d> scalar = scalarf.GetCellVarArr(z.Inner);
                if (scalar != null)
                {
                    FvInnCell[] cells = z.InnCells;
                    int endp1 = z.Inner.CellIdx.End;
                    for (int i1 = z.Inner.CellIdx.Start; i1 < endp1; i1++)
                    {
                        Vector3d cellVar = scalar.GetCellVar(i1);
                        int segId = (int)((cellVar.Length() - range.Min) / segRange);
                        segId = Math.Min(segId, maxValidId);
                        AddArrow(meshGeometry3Ds[segId], cells[i1].Center, scale * cellVar);
                    }
                }
            }
            ToModelVisual3D(meshGeometry3Ds, ColorRange);
        }
        private void AddArrow(MeshGeometry3D meshGeom, Vector3d basePoint, Vector3d vector)
        {
            if (vector.LengthSquared() == 0) return;
            Point3DCollection points = meshGeom.Positions;
            int pstart = points.Count;          
            arrowFactory.CreateArrow(V2P(basePoint), V2V(vector));
            foreach (var fn in arrowFactory.Positions)
            {
                points.Add(fn);
            }
            Int32Collection triInd = meshGeom.TriangleIndices;
            foreach (var i in arrowFactory.TriangleIndices)
            {
                triInd.Add(pstart + i);
            }
        }
        public void CreateGraph_Mesh()
        {
            //计算每个分段内数量，用于预分配capacity
            ColorRange colRange = new ColorRange();
            int capacity = 0;
            foreach (var b in Graph.SelectedBound())
            {
                capacity += b.FaceIdx.GetCount();
            }
            if (capacity == 0) return;
            int preCount = (capacity / colRange.Segment + 10) * 3;
            //判断属于哪个分段并添加到相应的mesh中
            MeshGeometry3D[] meshGeometry3Ds = new MeshGeometry3D[colRange.Segment];
            for (int i1 = 0; i1 < meshGeometry3Ds.Length; i1++)
            {
                meshGeometry3Ds[i1] = new MeshGeometry3D()
                {
                    Positions = new Point3DCollection(preCount),
                    TriangleIndices = new Int32Collection(preCount)
                };
            }
            int segment = colRange.Segment;
            foreach (var b in Graph.SelectedBound())
            {
                FvBouFace[] faces = b.InZone.BouFaces;
                Vector3d[] nodes = b.InZone.Nodes;
                int endp1 = b.FaceIdx.End;
                for (int i1 = b.FaceIdx.Start; i1 < endp1; i1++)
                {
                    int segId = i1 % segment;
                    AddPlane(meshGeometry3Ds[segId], nodes, faces[i1].Nodes);
                }
            }
            ToModelVisual3D(meshGeometry3Ds, colRange);
        }
        public void CreateGraph_Scalar()
        {
            ColorRange = new ColorRange();
            ScalarFieldFinder scalarf = new ScalarFieldFinder(Graph.ScalarType);
            //计算min max值
            MinMaxRange range = new();
            MultiThreads.RunParallelTask((threadInfo) =>
            {
                MinMaxRange tempR = new MinMaxRange();
                foreach (var b in Graph.SelectedBound())
                {
                    IFaceVarArr<double> scalar = scalarf.GetFaceVarArr(b);
                    if (scalar != null)
                    {
                        (int i1, int endp1) = b.FaceIdx.Split(threadInfo);
                        for (; i1 < endp1; i1++)
                        {
                            tempR.Include(scalar.GetFaceVar(i1));
                        }
                    }
                }
                lock (MultiThreads.GlobalLock)
                {
                    range.Include(tempR);
                }
            });
            if (range.IsEmpty()) return;
            ColorRange.Range = range;
            double segRange = range.GetRange() == 0 ? 1 : range.GetRange() / ColorRange.Segment;//每段的长度
            //计算每个分段内数量，用于预分配capacity
            int[] segCapacity = new int[ColorRange.Segment + 1];//最后加一个gray色
            MultiThreads.RunParallelTask((threadInfo) =>
            {
                int[] tempSetCapacity = new int[ColorRange.Segment + 1];
               
                int maxValidId = ColorRange.Segment - 2;
                int invalidId = ColorRange.Segment - 1;
                foreach (var b in Graph.SelectedBound())
                {
                    IFaceVarArr<double> scalar = scalarf.GetFaceVarArr(b);
                    if (scalar != null)
                    {
                        (int i1, int endp1) = b.FaceIdx.Split(threadInfo);
                        for (; i1 < endp1; i1++)
                        {
                            double faceVar = scalar.GetFaceVar(i1);
                            int segId = (int)((faceVar - range.Min) / segRange);
                            tempSetCapacity[Math.Max(Math.Min(segId, maxValidId), 0)]++;
                        }
                    }
                }
                lock (MultiThreads.GlobalLock)
                {
                    for (int i = 0; i < segCapacity.Length; i++)
                    {
                        segCapacity[i] += tempSetCapacity[i];
                    }
                }
            });
            //判断属于哪个分段并添加到相应的mesh中
            MeshGeometry3D[] meshGeometry3Ds = new MeshGeometry3D[ColorRange.Segment + 1];//最后加一个gray色
            for (int i1 = 0; i1 < meshGeometry3Ds.Length; i1++)
            {
                meshGeometry3Ds[i1] = new MeshGeometry3D()
                {
                    Positions = new Point3DCollection(segCapacity[i1] * 3),
                    TriangleIndices = new Int32Collection(segCapacity[i1] * 3)
                };
            }
            PlaneFactory planeFactory = new PlaneFactory();
            //double segRange = range.GetRange() / ColorRange.Segment;
            int maxValidId = ColorRange.Segment - 2;
            int invalidId = ColorRange.Segment - 1;
            foreach (var b in Graph.SelectedBound())
            {
                IFaceVarArr<double> scalar = scalarf.GetFaceVarArr(b);
                if (scalar != null)
                {
                    FvBouFace[] faces = b.InZone.BouFaces;
                    Vector3d[] nodes = b.InZone.Nodes;
                    int endp1 = b.FaceIdx.End;
                    for (int i1 = b.FaceIdx.Start; i1 < endp1; i1++)
                    {
                        double faceVar = scalar.GetFaceVar(i1);
                        int segId = (int)((faceVar - range.Min) / segRange);
                        segId = Math.Min(segId, maxValidId);
                        AddPlane(meshGeometry3Ds[segId], nodes, faces[i1].Nodes);
                    }
                }
            }
            ToModelVisual3D(meshGeometry3Ds, ColorRange);
        }
        private void ToModelVisual3D(MeshGeometry3D[] meshGeometry3Ds, ColorRange colorRange)
        {
            //添加到modelgroup
            Model3DGroup ModelGroup = new Model3DGroup();
            //彩色部分
            for (int i1 = 0; i1 < colorRange.Segment; i1++)
            {
                if (meshGeometry3Ds[i1].Positions.Count == 0) continue;
                DiffuseMaterial material = new DiffuseMaterial(new SolidColorBrush(colorRange.ColorBarColors[i1]));
                ModelGroup.Children.Add(new GeometryModel3D()
                {
                    Geometry = meshGeometry3Ds[i1],
                    Material = material,
                    BackMaterial = material,
                });
            }
            //灰色部分
            if (meshGeometry3Ds.Length > colorRange.Segment && meshGeometry3Ds[colorRange.Segment].Positions.Count != 0)
            {
                DiffuseMaterial material = new DiffuseMaterial(Brushes.Gray);
                ModelGroup.Children.Add(new GeometryModel3D()
                {
                    Geometry = meshGeometry3Ds[colorRange.Segment],
                    Material = material,
                    BackMaterial = material,
                });
            }
            if(ModelGroup.Children.Count == 0)
            {
                return;
            }
            ModelVisual = new ModelVisual3D()
            {
                Content = ModelGroup,
            };
        }
        private void AddPlane(MeshGeometry3D meshGeom, Vector3d[] nodes, int[] faceNodes)
        {
            Point3DCollection points = meshGeom.Positions;
            int pstart = points.Count;
            foreach (var fn in faceNodes)
            {
                points.Add(V2P(nodes[fn]));
            }
            Int32Collection triInd = meshGeom.TriangleIndices;
            foreach (var i in planFactory.GetTriInd(faceNodes.Length))
            {
                triInd.Add(pstart + i);
            }
        }
        private Point3D V2P(Vector3d v)
        {
            return new Point3D(v.X, v.Y, v.Z);
        }
        private Vector3D V2V(Vector3d v)
        {
            return new Vector3D(v.X, v.Y, v.Z);
        }
        public struct PlaneFactory
        {
            public PlaneFactory()
            {
                triInds = new int[30][];
                for (int i1 = 0; i1 < triInds.Length; i1++)
                {
                    int pointCount = i1 + 3;
                    int triCount = pointCount - 2;
                    int[] ti = triInds[i1] = new int[triCount * 3];
                    for (int i2 = 0; i2 < triCount; i2++)
                    {
                        int i23 = i2 * 3;
                        ti[i23] = 0;
                        ti[i23 + 1] = i2 + 1;
                        ti[i23 + 2] = i2 + 2;
                    }
                }
            }
            private int[][] triInds;
            public int[] GetTriInd(int pointCount)
            {
                return triInds[pointCount - 3];
            }
        }
    }
}

